<template>
  <div class="app-container">
    <div style="margin-bottom: 20px;">
      <!-- <div style="margin-bottom: 16px">还可登录账号数: {{ avalibleCount }}</div> -->
      <el-button @click="onAddNormalAccountTap">登录账号</el-button>
      <!-- <el-button @click="onCreateWalletTap">一键链接钱包</el-button> -->
      <el-button @click="onAddWalletsButton">批量添加钱包</el-button>
      <el-button @click="onAddEthWalletsButton">批量添加ETH格式钱包</el-button>
      <el-button @click="onApiAccountTap">加API账号</el-button>
      <!-- <el-button>批量加普通账号</el-button>
      <el-button>批量加API账号</el-button> -->
    </div>
    <el-table v-loading="isLoading" :data="accountsList" element-loading-text="Loading" border fit
      highlight-current-row>
      <el-table-column align="center" label="Index" width="95">
        <template slot-scope="scope">
          {{ scope.$index }}
        </template>
      </el-table-column>
      <el-table-column prop="id" label="ID" width="95">
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="160">
      </el-table-column>
      <el-table-column prop="password" label="密码" width="120">
      </el-table-column>
      <!-- <el-table-column prop="session" label="登录状态" show-overflow-tooltip width="120" align="center">
        <template slot-scope="scope">
          <el-tag type="danger" effect="dark" v-if="scope.row.session == null">
            未登录
          </el-tag>
          <el-tag type="success" effect="dark" v-else>
            已登录
          </el-tag>
        </template>
      </el-table-column> -->
      <el-table-column prop="session" label="TG-Session" show-overflow-tooltip>
      </el-table-column>
      <el-table-column prop="proxy" label="代理">
      </el-table-column>
      <el-table-column prop="wallet_address" label="钱包地址">
      </el-table-column>
      <el-table-column prop="wallet_address_user_friendly" label="钱包地址(user_friendly格式)">
      </el-table-column>
      <el-table-column prop="eth_wallet_address" label="ETH钱包地址">
      </el-table-column>
      <!-- <el-table-column prop="api" label="API-URL" width="240">
      </el-table-column> -->
      <el-table-column prop="updatetime" label="登录时间" width="200">
      </el-table-column>
      <el-table-column fixed="right" label="操作" width="260">
        <template slot-scope="scope">
          <el-button @click="onUpdateClick(scope.row)" type="text" size="small">编辑</el-button>
          <el-button @click="onLoginClick(scope.row)" type="text" size="small">登录</el-button>
          <el-button @click="onAcceptCodeClick(scope.row)" type="text" size="small">接码</el-button>
          <el-button @click="onDeleteClick(scope.row)" type="text" size="small">删除</el-button>
          <el-button @click="onResetLoginClick(scope.row)" type="text" size="small">其他设备下线</el-button>
        </template>
      </el-table-column>
    </el-table>
    <account-dialog :account="selectAccount" :visible="this.showAccount"
      @on-dismiss="onAccountDialogDismiss"></account-dialog>
    <api-account-dialog :visible="this.showApiAccount" @on-dismiss="onApiAccountDialogDismiss"></api-account-dialog>
    <add-wallets :visible="this.showAddWalletsDialog" @on-dismiss="onAddWalletsDialogDismiss"></add-wallets>
    <add-eth-wallets :visible="this.showAddEthWalletsDialog" @on-dismiss="onAddEthWalletsDialogDismiss"></add-eth-wallets>
  </div>
</template>

<script>

import AddWallets from "./coponents/wallets";
import AddEthWallets from "./coponents/ethWallets";
import AccountDialog from "./coponents/AccountDialog";
import ApiAccountDialog from "./coponents/ApiAccountDialog";
import { TelegramClient, Api } from 'telegram'
import { StringSession } from 'telegram/sessions'
import Config from "@/utils/config"
import IpcParams, { gEventTonwebCreate } from "@/ipc/IpcParams";
const xpath = require('xpath');
const dom = require('@xmldom/xmldom').DOMParser;
import DexieCtx from "@/utils/dexiectx";



export default {
  components: {
    AccountDialog,
    ApiAccountDialog,
    AddWallets,
    AddEthWallets,
  },
  data() {
    return {
      isLoading: false,
      selectAccount: {},
      walletsList: {},
      showAccount: false,
      showApiAccount: false,
      showAddWalletsDialog: false,
      showAddEthWalletsDialog: false,
    }
  },
  created() {
    this.$store.dispatch("loadAccounts")
    this.$store.dispatch("user/loadActives")
  },
  computed: {
    accountsList() {
      return this.$store.state.accounts.accountsList;
    },
    activeCount() {
      let count = 0
      const activeList = this.$store.state.user.activeList;
      for (let index = 0; index < (activeList || []).length; index++) {
        const ac = activeList[index];
        count = count + parseInt(ac.account)
      }
      return count
    },
    // 还可登录的账号数
    // 默认5个
    avalibleCount() {
      const avCount = this.activeCount - (this.accountsList || []).length
      return avCount + 5
    }
  },
  methods: {
    onAddWalletsButton() {
      this.showAddWalletsDialog = true
    },
    onAddEthWalletsButton() {
      this.showAddEthWalletsDialog = true
    },
    onCreateWalletTap() {
      const ipcParams = new IpcParams(gEventTonwebCreate, {count: this.accountsList.length})
      const friendly_address = window.tonwebAPI.invoke(ipcParams)
    },
    async onAddNormalAccountTap() {
      while (true) {
        if (this.avalibleCount <= 0) {
          this.$message({
            type: "error",
            message: "激活账号数已用完"
          })
          break
        }
        try {
          await this.loginNormalAccount(null)
        } catch (error) {
          break
        }
      }
    },
    onAccountDialogDismiss() {
      this.showAccount = false
      this.selectAccount = null
    },
    async onAddWalletsDialogDismiss(wallets) {
      console.log('dismiss .....')
      this.showAddWalletsDialog = false
      // this.showAddEthWalletsDialog = false

      if ((wallets ?? "") == "") {
        return
      }

      // loop all existing tg accounts
      const dexie = await DexieCtx.db()
      const accountsList = await dexie.accounts.toArray()

      const walletArray = wallets.split("\n")

      for (let index = 0; index < accountsList.length; index++) {
        if ( walletArray[index] != null ) {
          const user_friendly_wallet = await window.tonwebAPI.invoke(new IpcParams("transfer_wallet_to_friendly", walletArray[index]))
          accountsList[index].wallet_address =  walletArray[index]
          accountsList[index].wallet_address_user_friendly = user_friendly_wallet
          await dexie.accounts.where("phone").equals(accountsList[index].phone).modify(accountsList[index])
        }
      }

      this.$store.dispatch("loadAccounts")
    },
    async onAddEthWalletsDialogDismiss(ethWallets) {
      console.log('dismiss .....')
      this.showAddEthWalletsDialog = false

      if ((ethWallets ?? "") == "") {
        return
      }

      // loop all existing tg accounts
      const dexie = await DexieCtx.db()
      const accountsList = await dexie.accounts.toArray()

      const ethWalletArray = ethWallets.split("\n")

      for (let index = 0; index < accountsList.length; index++) {
        let index1 = index * 3;
        let index2 = index * 3 + 1;
        let index3 = index * 3 + 2;
        if ( ethWalletArray[index1] != null ) {
          // const user_friendly_wallet = await window.tonwebAPI.invoke(new IpcParams("transfer_wallet_to_friendly", walletArray[index]))
          accountsList[index].eth_wallet_address =  ethWalletArray[index2]
          accountsList[index].eth_wallet_signature = ethWalletArray[index3]
          await dexie.accounts.where("phone").equals(accountsList[index].phone).modify(accountsList[index])
        }
      }

      this.$store.dispatch("loadAccounts")
    },
    onUpdateClick(account) {
      this.selectAccount = account
      this.showAccount = true
    },
    onDeleteClick(account) {
      this.$confirm('确定删除账号吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        await this.$store.dispatch("deleteAccount", account)
        this.$message({
          type: 'success',
          message: '删除成功!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    },
    // 手动登录不走代理
    async onLoginClick(account) {
      if ((account.api ?? "") == "") {
        this.loginNormalAccount(account)
      } else {
        this.loginApiAccount(account)
      }
    },
    async onAcceptCodeClick(account) {
      const strSession = new StringSession(account.session)
      const client = new TelegramClient(strSession, Config.API_ID, Config.API_HASH, {
        connectionRetries: 3,
        useWSS: true,
        timeout: 20,
        // proxy: {
        //   ip: "103.177.239.117", // Proxy host (IP or hostname)
        //   port: 6579, // Proxy port
        //   username: 'jkyeytls',
        //   password: 'm46r6yilb1gc',
        //   MTProxy: false, // Whether it's an MTProxy or a normal Socks one
        //   socksType: 5, // If used Socks you can choose 4 or 5.
        //   timeout: 2, // Timeout (in seconds) for connection,
        // },
      })
      try {
        this.isLoading = true
        await client.connect()
        const isAuth = await client.checkAuthorization()
        if (isAuth) {
          const tele = await client.getInputEntity(777000);
          const msgs = await client.getMessages(tele, {
            // fromUser: tele,
            limit: 1,
            // search: "Login code:"
          })
          if (msgs != null && msgs.length > 0) {
            const loginCodeMsg = msgs[0]
            const date = new Date(loginCodeMsg.date * 1000)
            const dateStr = date.toLocaleString()
            this.$message({
              type: "info",
              message: dateStr + "\n" + loginCodeMsg.text,
            })
            console.log(msgs[0])
          } else {
            throw new Error("未收到任何验证码消息")
          }
        } else {
          // account.session = null
          // this.$store.dispatch("updateAccount", account)
          throw new Error("该账号登录已失效");
        }
      } catch (error) {
        console.error(error)
        this.$message({
          type: "error",
          message: error.message || error,
        })
      } finally {
        await client.disconnect()
        await client.destroy()
        this.isLoading = false
      }
    },
    async loginNormalAccount(account) {
      try {
        this.isLoading = true
        let accountCopy = account == null ? {} : { ...account }
        if (accountCopy.phone == null) {
          let phone = await this.requestInputField("请输入电话号码")
          phone = phone.replace("+", "")
          phone = phone.replace(" ", "")
          accountCopy.phone = phone
        }
        const strSession = new StringSession() //create a new StringSession, also you can use StoreSession
        const client = new TelegramClient(strSession, Config.API_ID, Config.API_HASH, {
          connectionRetries: 3,
          useWSS: true,
          timeout: 20,
        })
        // 重要一定要设置client.phone
        client.phone = accountCopy.phone
        await client.start({
          forceSMS: false,
          phoneNumber: accountCopy.phone,
          password: (accountCopy.password ?? '') != '' ? accountCopy.password : async () => {
            const password = await this.requestInputField("请输入2FA密码")
            accountCopy.password = password
            return password
          },
          phoneCode: async () => await this.requestInputField("请输入验证码"),
          onError: (error) => {
            throw error;
          },
        });
        accountCopy.session = client.session.save()
        console.log(client.phone, client.session.save()); // Save this string to avoid logging in again
        await client.sendMessage("me", { message: "Hello World!" });
        accountCopy.updatetime = (new Date()).toLocaleString()
        await this.$store.dispatch("addAccount", accountCopy)
      } catch (error) {
        console.error(error)
        this.$message({
          type: 'error',
          message: error.message || error,
        })
        throw error;
      } finally {
        this.isLoading = false
        try {
          await client.disconnect()
          await client.destroy()
        } catch (error) {
        }
      }
    },
    async onResetLoginClick(account) {
      const strSession = new StringSession(account.session)
      const client = new TelegramClient(strSession, Config.API_ID, Config.API_HASH, {
        connectionRetries: 3,
        useWSS: true,
        timeout: 20,
      })
      try {
        this.isLoading = true
        await client.connect()
        const isAuth = await client.checkAuthorization()
        if (isAuth) {
          const result = await client.invoke(new Api.auth.ResetAuthorizations({}));
          console.log(result); // prints the result
          if (result) {
            this.$message({
              type: "success",
              message: "已将其他设备成功踢下线"
            })
          }
        } else {
          // account.session = null
          // this.$store.dispatch("updateAccount", account)
          throw new Error("该账号登录已失效");
        }
      } catch (error) {
        console.error(error)
        this.$message({
          type: "error",
          message: error.message || error,
        })
      } finally {
        await client.disconnect()
        await client.destroy()
        this.isLoading = false
      }
    },
    async loginApiAccount(account) {
      const strSession = new StringSession() //create a new StringSession, also you can use StoreSession
      const client = new TelegramClient(strSession, Config.API_ID, Config.API_HASH, {
        connectionRetries: 3,
        useWSS: true,
        timeout: 20,
      })
      try {
        this.isLoading = true
        let accountCopy = account == null ? {} : { ...account }
        if (accountCopy.phone == null) {
          throw new Error("API账号缺失电话号码");
        }
        // 重要一定要设置client.phone
        client.phone = accountCopy.phone
        const apiUrl = accountCopy.api
        let authInfoDic = null
        await client.start({
          phoneNumber: accountCopy.phone,
          password: (accountCopy.password ?? '') != '' ? accountCopy.password : async () => {
            return authInfoDic.password
          },
          phoneCode: async () => {
            const result = await this.requestAuthInfoByUrl(apiUrl)
            authInfoDic = result
            return result.phoneCode
          },
          onError: (error) => {
            throw error;
          },
        });
        accountCopy.session = client.session.save()
        accountCopy.password = authInfoDic.password
        console.log(client.phone, client.session.save()); // Save this string to avoid logging in again
        await client.sendMessage("me", { message: "Hello World!" });
        accountCopy.updatetime = (new Date()).toLocaleString()
        await this.$store.dispatch("addAccount", accountCopy)
      } catch (error) {
        console.error(error)
        throw error;
      } finally {
        this.isLoading = false
        try {
          await client.disconnect()
          await client.destroy()
        } catch (error) {
        }
      }
    },
    async requestAuthInfoByUrl(apiUrl) {
      return new Promise(async (resolve, reject) => {
        const self = this
        setTimeout(async () => {
          self.request(apiUrl)
            .then(htmlStr => {
              const result = self.parseCodeAndPassword(htmlStr)
              resolve(result)
            })
            .catch(error => {
              console.error(error)
              reject(error)
            })
        }, 5000);
      })
    },
    parseCodeAndPassword(htmlStr) {
      var doc = new dom().parseFromString(htmlStr, 'text/html');
      var nodes = xpath.select("//*[@id=\"code\"]", doc);
      let result = {}
      if ((nodes ?? []).length == 0) {
        return result;
      }
      let phoneCode = nodes[0].firstChild.nodeValue
      result.phoneCode = phoneCode

      var passNodes = xpath.select("//*[@id=\"password\"]", doc);
      if ((passNodes ?? []).length == 0) {
        return result;
      }
      const password = passNodes[0].firstChild.nodeValue
      result.password = password
      console.log(new Date(), "parse code", result)
      return result
    },
    async requestInputField(title) {
      return new Promise((resolve, reject) => {
        this.$prompt(title, '账号登录', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          // inputPattern: /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
          // inputErrorMessage: '邮箱格式不正确'
        }).then(({ value }) => {
          resolve(value)
        }).catch(() => {
          reject(title)
        });
      });
    },
    onApiAccountTap() {
      this.showApiAccount = true
    },
    async onApiAccountDialogDismiss(apiText) {
      this.showApiAccount = false
      if ((apiText ?? "") == "") {
        return;
      }
      const apiArr = apiText.split("\n")
      let apiAccountList = apiArr.map((str) => {
        const phoneMatches = str.match(/[0-9]+/g)
        const urlMathces = str.match(/(https|http):\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?/g)
        const sepArr = str.split("|")
        if (phoneMatches == null || urlMathces == null) {
          return {
            error: str,
          }
        }
        let phone = phoneMatches[0]
        let apiUrl = urlMathces[0]
        return {
          phone,
          api: apiUrl,
        }
      })
      // 过滤真实格式账号
      const errorList = apiAccountList.filter((ac) => {
        return ac.error != null;
      })
      if (errorList.length > 0) {
        this.$message({
          type: "error",
          message: "错误账号: " + errorList[0].error,
        })
        return
      }
      apiAccountList = apiAccountList.filter((ac) => {
        return ac.phone != null;
      })
      for (let index = 0; index < apiAccountList.length; index++) {
        const ac = apiAccountList[index];
        try {
          await this.loginApiAccount(ac)
        } catch (error) {
          this.$message({
            type: "error",
            message: error
          })
          continue
        }
      }
    },
  }
}
</script>